// ----------------------------------------
// Filename: threads.c
// Description: The implementation of thread functions
// Author: Jon Maloney
// Date: 9-May-2012
//

//************************************************************
//                    Include .h files
//************************************************************
#include "thread.h"
#include "scheduler.h"
#include "p24FJ64GA004.h"
#include <stdlib.h>
//************************************************************
//                    Include external references
//************************************************************

//************************************************************
//                    Define Statements
//************************************************************

//************************************************************
//                    Struct, Union, enum Statements
//************************************************************

//************************************************************
//                    Global variables and constants
//************************************************************

//************************************************************
//                    Prototype Functions
//************************************************************

//************************************************************
//                    Implementation
//************************************************************

// ----------------------------------------
// Thread_Create
// 
// Allocates memory and sets up the stack for a new task.
//	The new task is then added to the ready queue.
// Input:
//	firstThreadFunction:	The first funciton that the thread will run
//							when created.
//  functionArgument:		An argument that can be passed to the thread.
//							The argument can be of any type. When the thread
//							is run the funcitonArgument must be typecast back
//							to its appropriate type before it can be used.
//	stackSize:				The size in bytes of the stack to be used by the
//							task.
//	priority				The tasks priority. The task priority can be of any
//							value > 0 and < configNB_PRIORITY_LEVELS (found in configuraiton.h).
//							Priorities are in assending order, that is a task
//							with a priority of 8 will have higher priority than 
//							a task of priorty 7.
//							If the user assignes a priorty level higher than configKERNEL_INTERRUPT_PRIORITY
//							the task will re-assign the priority to be equal to configKERNEL_INTERRUPT_PRIORITY.
// Output:
//  A handle to the thread control block (TCB) that was created.
// Conditions:
// 	none
//
TCB * Thread_Create(void *firstThreadFunction, void *functionArgument, UINT8 stackSize, PRIORITY priority)
{
	//allocate some memory for the TCB
	TCB * newTask = (TCB *)malloc(sizeof(TCB));

	//Check to make sure malloc worked
	if(newTask == NULL)
		return NULL;

	//Allocate some memory for the stack
	newTask->stackPointer = (UINT16 *)malloc(stackSize * sizeof(UINT16));

	//Check to make sure that malloc worked
	if(newTask->stackPointer == NULL)
		return NULL;
	
	//If the thread priority exceeds its limit
	if(priority >= configKERNEL_INTERRUPT_PRIORITY)
		priority = configKERNEL_INTERRUPT_PRIORITY;	//Give the thread a maximum allowable priority
	
	newTask->Priority = priority;		//Set the priority in the threads TCB

	//Fill the stack with some values pretending that we have done a context switch
	*newTask->stackPointer++ = (unsigned int)firstThreadFunction;	/*Place the lower 16 bits of the function pointer into the stack*/
																	/*No need to place <22:16> on the stack as the linker uses handles.
														 			*Handles are used when function pointer values need more than 16 bits.
														 			*If more than 16 bits are needed to resolve an address the linker places
														 			*a jump table at the begining of the code segment. Function pointers are 
														 			*mapped to the jump table. A call to a function pointer gets diverted to 
														 			*a jump table which contains a goto statement that again re-directs
														 			*the program counter to correct code segment*/
	*newTask->stackPointer++ = 0;									/* to unlink the frame pointer */
	*newTask->stackPointer++ = (UINT16)0;							/* inital status register value*/
	*newTask->stackPointer++ = (UINT16)functionArgument;			/*Pointer to the function argument */
	*newTask->stackPointer++ = 0x1111;								/* W1 */
	*newTask->stackPointer++ = 0x2222; 								/* W2 */
	*newTask->stackPointer++ = 0x3333; 								/* W3 */
	*newTask->stackPointer++ = 0x4444; 								/* W4 */
	*newTask->stackPointer++ = 0x5555; 								/* W5 */
	*newTask->stackPointer++ = 0x6666; 								/* W6 */
	*newTask->stackPointer++ = 0x7777; 								/* W7 */
	*newTask->stackPointer++ = 0x8888; 								/* W8 */
	*newTask->stackPointer++ = 0x9999; 								/* W9 */
	*newTask->stackPointer++ = 0xaaaa; 								/* W10 */
	*newTask->stackPointer++ = 0xbbbb; 								/* W11 */
	*newTask->stackPointer++ = 0xcccc; 								/* W12 */
	*newTask->stackPointer++ = 0xdddd; 								/* W13 */
	*newTask->stackPointer++ = 0xeeee; 								/* W14 */
	*newTask->stackPointer++ = 0xcdce; 								/* RCOUNT */
	*newTask->stackPointer++ = 0xabac; 								/* TBLPAG */
	*newTask->stackPointer++ = CORCON;								/* CORCON */
	*newTask->stackPointer++ = PSVPAG;								/* PSVPAG */	
	*newTask->stackPointer++ = 0x0000;								/* The critical nesting depth. */

	newTask->tasksLinkedListNode = Scheduler_AddTaskToReadyQueue(newTask);	//Tell the scheduler a new task has been created.
	 
	return newTask;	//Return a handle to the thread control block.
}

// ----------------------------------------
// Thread_Create
// 
// Allocates memory and sets up the stack for a new task.
//	The new task is then added to the ready queue.
// Input:
//	threadToDelete: A pointer to the thread to be deleted.
// Output:
//  none
// Conditions:
// 	none
//
void Thread_Delete(TCB * threadToDelete)
{	
	//Free up the memory allocated to the stack
	free(threadToDelete->stackPointer);
	
	threadToDelete->stackPointer = 0;
	threadToDelete->Priority = 0;
	threadToDelete->currentState = 0;
	threadToDelete->WakeOnTickValue = 0;
	
	//Frees up the memory allocated to the taskNode and TCB
	Scheduler_DeleteTask(threadToDelete->tasksLinkedListNode);

	return;
}
